﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace BitmapTagger
{
	/// <summary>
	/// Acts as a dictionary for the different exif values and their meanings,
	/// this is a singleton
	/// </summary>
	public class ExifValues
	{

		/// <summary>
		/// Meaning for when a value is null
		/// </summary>
		public const string NULL_MEANING = "";

		/// <summary>
		/// Create a new ExifValue
		/// </summary>
		private ExifValues()
		{
			InitializeDictionaries();
		}

		/// <summary>
		/// The singleton instance
		/// </summary>
		private static ExifValues instance;

		/// <summary>
		/// Get the singleton instance of ExifValues
		/// </summary>
		public static ExifValues Instance
		{
			get
			{
				if (instance == null)
					instance = new ExifValues();
				return instance;
			}
		}

		/// <summary>
		/// Initialize the dictionaries
		/// </summary>
		private void InitializeDictionaries()
		{
			#region sensing method
			SensingMethodDictionary = new Dictionary<ushort?, string>();
			SensingMethodDictionary.Add(1, "Not Defined");
			SensingMethodDictionary.Add(2, "One-Chip Color Area Sensor");
			SensingMethodDictionary.Add(3, "Two-Chip Color Area Sensor");
			SensingMethodDictionary.Add(4, "Three-Chip Color Area Sensor");
			SensingMethodDictionary.Add(5, "Color Sequential Area Sensor");
			SensingMethodDictionary.Add(7, "Trilinear Sensor");
			SensingMethodDictionary.Add(8, "Color Sequential Linear Sensor");
			#endregion
			#region light source
			LightSourceDictionary = new Dictionary<ushort?, string>();
			LightSourceDictionary.Add(0, "Unknown");
			LightSourceDictionary.Add(1, "Daylight");
			LightSourceDictionary.Add(2, "Fluorescent");
			LightSourceDictionary.Add(3, "Tungsten (Incandescent Light)");
			LightSourceDictionary.Add(4, "Flash");
			LightSourceDictionary.Add(9, "Fine weather");
			LightSourceDictionary.Add(10, "Cloudy weather");
			LightSourceDictionary.Add(11, "Shade");
			LightSourceDictionary.Add(12, "Daylight fluorescent (D 5700 – 7100K)");
			LightSourceDictionary.Add(13, "Day white fluorescent (N 4600 – 5400K)");
			LightSourceDictionary.Add(14, "Cool white fluorescent (W 3900 – 4500K)");
			LightSourceDictionary.Add(15, "White fluorescent (WW 3200 – 3700K)");
			LightSourceDictionary.Add(17, "Standard light A");
			LightSourceDictionary.Add(18, "Standard light B");
			LightSourceDictionary.Add(19, "Standard light C");
			LightSourceDictionary.Add(20, "D55");
			LightSourceDictionary.Add(21, "D65");
			LightSourceDictionary.Add(22, "D75");
			LightSourceDictionary.Add(23, "D50");
			LightSourceDictionary.Add(24, "ISO Studio Tungsten");
			LightSourceDictionary.Add(255, "Other Light Source");
			#endregion
			#region exposure program
			ExposureProgramDictionary = new Dictionary<ushort?, string>();
			ExposureProgramDictionary.Add(0, "Not defined");
			ExposureProgramDictionary.Add(1, "Manual");
			ExposureProgramDictionary.Add(2, "Normal Program");
			ExposureProgramDictionary.Add(3, "Aperture Priority");
			ExposureProgramDictionary.Add(4, "Shutter Priority");
			ExposureProgramDictionary.Add(5, "Creative Program");
			ExposureProgramDictionary.Add(6, "Action Program");
			ExposureProgramDictionary.Add(7, "Portrait Mode");
			ExposureProgramDictionary.Add(8, "Landscape Mode");
			#endregion
			#region metering mode
			MeteringModeDictionary = new Dictionary<ushort?, string>();
			MeteringModeDictionary.Add(0, "Unknown");
			MeteringModeDictionary.Add(1, "Average");
			MeteringModeDictionary.Add(2, "Center Weighted Average");
			MeteringModeDictionary.Add(3, "Spot");
			MeteringModeDictionary.Add(4, "MultiSpot");
			MeteringModeDictionary.Add(5, "Pattern");
			MeteringModeDictionary.Add(6, "Partial");
			MeteringModeDictionary.Add(255, "Other"); 
			#endregion
			#region resolution unit
			ResolutionUnitDictionary = new Dictionary<ushort?, string>();
			ResolutionUnitDictionary.Add(2, "in");
			ResolutionUnitDictionary.Add(3, "cm");
			#endregion
		}


		#region sensing method

		/// <summary>
		/// Get or set the dictionary for sensing method values and their meanings
		/// </summary>
		private Dictionary<ushort?, string> SensingMethodDictionary
		{
			get;
			set;
		}

		/// <summary>
		/// Get the possible values for sensing method
		/// </summary>
		public List<ushort?> SensingMethodValues
		{
			get
			{
				var collection = SensingMethodDictionary.Keys;
				List<ushort?> values = new List<ushort?>();
				foreach (ushort? value in collection)
					values.Add(value);

				return values;
			}
		}

		/// <summary>
		/// Get whether the value is a valid value for sensing method 
		/// </summary>
		/// <param name="value">The value to check</param>
		/// <returns>True if it is a valid value for sensing method, false if not</returns>
		public bool IsValidSensingMethodValue(ushort? value)
		{
			if (value == null || SensingMethodDictionary.ContainsKey(value))
				return true;
			else
				return false;
		}

		/// <summary>
		/// Get the meaning for a sensing method value
		/// </summary>
		/// <param name="value">The value to get the meaning of</param>
		/// <returns>The meaning of othe passed value</returns>
		/// <precondition>Must be a valid sensing method value, 
		/// IsValidSensingMethodValue() to check</precondition>
		public string GetSensingMethodMeaning(ushort? value)
		{
			Debug.Assert(IsValidSensingMethodValue(value), "Not a valid sensing method value");
			if (value == null)
				return NULL_MEANING;
			else
				return SensingMethodDictionary[value];
		}

		/// <summary>
		/// Get the value for a sensing method meaning
		/// </summary>
		/// <param name="meaning">The meaning to check</param>
		/// <returns>The value if it exists</returns>
		/// <preconditions>Must be a valid meaning</preconditions>
		public ushort? GetSensingMethodValue(string meaning)
		{
			Debug.Assert(meaning != null &&
				(SensingMethodDictionary.ContainsValue(meaning) || meaning.Equals(NULL_MEANING)), 
				"Not a valid meaning for sensing method");
			if (meaning.Equals(NULL_MEANING))
				return null;
			else
			{
				return SensingMethodDictionary.FirstOrDefault( kvp => kvp.Value.Equals(meaning)).Key;
			}
		}

		#endregion

		#region light source

		/// <summary>
		/// Get or set the dictionary for light source values and their meanings
		/// </summary>
		private Dictionary<ushort?, string> LightSourceDictionary
		{
			get;
			set;
		}

		/// <summary>
		/// Get the possible values for light source
		/// </summary>
		public List<ushort?> LightSourceValues
		{
			get
			{
				var collection = LightSourceDictionary.Keys;
				List<ushort?> values = new List<ushort?>();
				foreach (ushort? value in collection)
					values.Add(value);

				return values;
			}
		}


		/// <summary>
		/// Get whether the value is a valid value for light source
		/// </summary>
		/// <param name="value">The value to check</param>
		/// <returns>True if it is a valid value for light source, false if not</returns>
		public bool IsValidLightSourceValue(ushort? value)
		{
			if (value == null ||  LightSourceDictionary.ContainsKey(value))
				return true;
			else
				return false;
		}

		/// <summary>
		/// Get the meaning for a light source value
		/// </summary>
		/// <param name="value">The value to get the meaning of</param>
		/// <returns>The meaning of othe passed value</returns>
		/// <precondition>Must be a valid light source value, 
		/// IsValidLightSourceValue() to check</precondition>
		public string GetLightSourceMeaning(ushort? value)
		{
			Debug.Assert(IsValidLightSourceValue(value), "Not a valid light source value");
			if (value == null)
				return NULL_MEANING;
			else
				return LightSourceDictionary[value];
		}

		/// <summary>
		/// Get the value for a light source meaning
		/// </summary>
		/// <param name="meaning">The meaning to check</param>
		/// <returns>The value if it exists</returns>
		/// <preconditions>Must be a valid meaning</preconditions>
		public ushort? GetLightSourceValue(string meaning)
		{
			Debug.Assert(meaning != null &&
				(LightSourceDictionary.ContainsValue(meaning) || meaning.Equals(NULL_MEANING)),
				"Not a valid meaning for sensing method");
			if (meaning.Equals(NULL_MEANING))
				return null;
			else
				return LightSourceDictionary.FirstOrDefault(kvp => kvp.Value.Equals(meaning)).Key;
		}

		#endregion

		#region exposure program

		/// <summary>
		/// Get or set the dictionary for exposure program values and their meanings
		/// </summary>
		private Dictionary<ushort?, string> ExposureProgramDictionary
		{
			get;
			set;
		}

		/// <summary>
		/// Get the possible values for the exposure program
		/// </summary>
		public List<ushort?> ExposureProgramValues
		{
			get
			{
				var collection = ExposureProgramDictionary.Keys;
				List<ushort?> values = new List<ushort?>();
				foreach (ushort? value in collection)
					values.Add(value);

				return values;
			}
		}

		/// <summary>
		/// Get whether the value is a valid value for exposure program
		/// </summary>
		/// <param name="value">The value to check</param>
		/// <returns>True if it is a valid value for light source, false if not</returns>
		public bool IsValidExposureProgramValue(ushort? value)
		{
			if (value == null || ExposureProgramDictionary.ContainsKey(value))
				return true;
			else
				return false;
		}

		/// <summary>
		/// Get the meaning for a exposure program value
		/// </summary>
		/// <param name="value">The value to get the meaning of</param>
		/// <returns>The meaning of othe passed value</returns>
		/// <precondition>Must be a valid exposure program value, 
		/// IsValidExposureProgramValue() to check</precondition>
		public string GetExposureProgramMeaning(ushort? value)
		{
			Debug.Assert(IsValidExposureProgramValue(value), "Not a valid exposure program value");
			if (value == null)
				return NULL_MEANING;
			else
				return ExposureProgramDictionary[value];
		}

		/// <summary>
		/// Get the value for a light source meaning
		/// </summary>
		/// <param name="meaning">The meaning to check</param>
		/// <returns>The value if it exists</returns>
		/// <preconditions>Must be a valid meaning</preconditions>
		public ushort? GetExposureProgramValue(string meaning)
		{
			Debug.Assert(meaning != null &&
				(ExposureProgramDictionary.ContainsValue(meaning) || meaning.Equals(NULL_MEANING)),
				"Not a valid meaning for exposure program");
			if (meaning.Equals(NULL_MEANING))
				return null;
			else
				return ExposureProgramDictionary.FirstOrDefault(kvp => kvp.Value.Equals(meaning)).Key;
		}

		#endregion

		#region metering mode

		/// <summary>
		/// Get or set the dictionary for metering mode values and their meanings
		/// </summary>
		private Dictionary<ushort?, string> MeteringModeDictionary
		{
			get;
			set;
		}

		/// <summary>
		/// Get the possible values for metering mode
		/// </summary>
		public List<ushort?> MeteringModeValues
		{
			get
			{
				var collection = MeteringModeDictionary.Keys;
				List<ushort?> values = new List<ushort?>();
				foreach (ushort? value in collection)
					values.Add(value);

				return values;
			}
		}

		/// <summary>
		/// Get whether the value is a valid value for metering mode
		/// </summary>
		/// <param name="value">The value to check</param>
		/// <returns>True if it is a valid value for metering mode, false if not</returns>
		public bool IsValidMeteringModeValue(ushort? value)
		{
			if (value == null || MeteringModeDictionary.ContainsKey(value))
				return true;
			else
				return false;
		}

		/// <summary>
		/// Get the meaning for a metering mode value
		/// </summary>
		/// <param name="value">The value to get the meaning of</param>
		/// <returns>The meaning of othe passed value</returns>
		/// <precondition>Must be a valid metering mode value, 
		/// IsValidExposureProgramValue() to check</precondition>
		public string GetMeteringModeMeaning(ushort? value)
		{
			Debug.Assert(IsValidMeteringModeValue(value), "Not a valid metering mode value");
			if (value == null)
				return NULL_MEANING;
			else
				return MeteringModeDictionary[value];
		}

		/// <summary>
		/// Get the value for a metering mode meaning
		/// </summary>
		/// <param name="meaning">The meaning to check</param>
		/// <returns>The value if it exists</returns>
		/// <preconditions>Must be a valid meaning</preconditions>
		public ushort? GetMeteringModeValue(string meaning)
		{
			Debug.Assert(meaning != null &&
				(MeteringModeDictionary.ContainsValue(meaning) || meaning.Equals(NULL_MEANING)),
				"Not a valid meaning for metering mode");
			if (meaning.Equals(NULL_MEANING))
				return null;
			else
				return MeteringModeDictionary.FirstOrDefault(kvp => kvp.Value.Equals(meaning)).Key;
		}

		#endregion

		#region resolution unit

		/// <summary>
		/// Get or set the dictionary for resolution unit values and their meanings
		/// </summary>
		private Dictionary<ushort?, string> ResolutionUnitDictionary
		{
			get;
			set;
		}

		/// <summary>
		/// Get whether the value is a valid value for resolution unit
		/// </summary>
		/// <param name="value">The value to check</param>
		/// <returns>True if it is a valid value for resolution unit, false if not</returns>
		public bool IsValidResolutionUnitValue(ushort? value)
		{
			if (value == null || ResolutionUnitDictionary.ContainsKey(value))
				return true;
			else
				return false;
		}

		/// <summary>
		/// Get the meaning for a resolution unit value
		/// </summary>
		/// <param name="value">The value to get the meaning of</param>
		/// <returns>The meaning of othe passed value</returns>
		/// <precondition>Must be a valid resolution unit value, 
		/// IsValidResolutionUnitValue() to check</precondition>
		public string GetResolutionUnitMeaning(ushort? value)
		{
			Debug.Assert(IsValidResolutionUnitValue(value), "Not a valid resolution unit value");
			if (value == null)
				return NULL_MEANING;
			else
				return ResolutionUnitDictionary[value];
		}

		/// <summary>
		/// Get the value for a resolution unit meaning
		/// </summary>
		/// <param name="meaning">The meaning to check</param>
		/// <returns>The value if it exists</returns>
		/// <preconditions>Must be a valid meaning</preconditions>
		public ushort? GetResolutionUnitValue(string meaning)
		{
			Debug.Assert(meaning != null &&
				(ResolutionUnitDictionary.ContainsValue(meaning) || meaning.Equals(NULL_MEANING)),
				"Not a valid meaning for resolution unit");
			
			
			if (meaning.Equals(NULL_MEANING))
				return null;
			else
				return ResolutionUnitDictionary.FirstOrDefault(kvp => kvp.Value.Equals(meaning)).Key;
		}

		#endregion

	}
}
